/***************************************************************************\
 * The Mattise Kernel: Userland Software									*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * main.c																	*
 *																			*
 * Entrypoint for the device filesystem (devfs)								*
 *																			*
\***************************************************************************/

/***************************************************************************
 * Includes
 ***************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <mmsg.h>
#include <process.h>
#include <vfs.h>

/***************************************************************************
 * Constants
 ***************************************************************************/

#define DEVFS_OPEN		VFS_OPEN
#define DEVFS_READ		VFS_READ
#define DEVFS_WRITE		VFS_WRITE
#define DEVFS_SEEK		VFS_SEEK
#define DEVFS_GETC		VFS_GETC
#define DEVFS_PUTC		VFS_PUTC
#define DEVFS_CLOSE		VFS_CLOSE
#define DEVFS_MOUNT		VFS_MOUNT
#define DEVFS_UMOUNT	VFS_UNMOUNT
#define DEVFS_REPMSG	VFS_REPMSG

#define DEVFS_STAT		VFS_STAT

/***************************************************************************
 * Types
 ***************************************************************************/

// A device connected to the devfs
struct devfs_Device {
	int handler_pid; // the handler for this device's I/O requests
	int state; // usable?
	char nm[64]; // device name (without /dev/ prefix)
	int nmlen; // length of the device name
} deviceList[1024]; // TODO: convert to a linked list?

/***************************************************************************
 * Functions
 ***************************************************************************/

// The device filesystem's request handler
void devfs_handler()
{
	// message size and message buffer
	int msize;
	char* mbuff;
	
	// already, we will have a message waiting (INITTHRD)
	struct RecvMsgData myMsg;
	while( !mattiseHasMessage() );
	
	// get the message
	mbuff = mattiseRecvMessage( &myMsg );

	// open the message, get the data
	struct vfs_ReqData* req = (struct vfs_ReqData*) mbuff;
	
	// the PID of the process for the open device
	int handlerpid = req->reqArg1;
	
	// the process that we're working with
	int myprocess = req->srcpid;
	
	// free the buffer
	mattiseFreeMessage( mbuff );
	
	// send out a message to our handler
	// so we can get the internal descriptor for it
	struct vfs_ReqData* request = (struct vfs_ReqData*) mattiseAllocMessageSz( sizeof( struct vfs_ReqData ) );
	request->msgcode = DEVFS_OPEN;
	request->srcpid = myprocess;
	request->datlen = sprintf( request->data, "%s", req->data );
	
	// send it
	mattiseSendMessage( handlerpid, DEVFS_OPEN, (char*) request, sizeof( struct vfs_ReqData ) );
	
	// wait for the reply
	while( 1 )
	{
		if( mattiseHasMessage() )
		{
			// get the message
			mbuff = mattiseRecvMessage( &myMsg );
			
			// verify that we do in fact have to exit
			if( myMsg.msgcode == DEVFS_REPMSG )
				break;
		}
	}
	
	// take it in, get the internal descriptor
	struct vfs_ReqData* drvrep = (struct vfs_ReqData*) mbuff;
	int int_desc = drvrep->descriptor;
	
	// free the buffer
	mattiseFreeMessage( mbuff );

	// now, finally, tell the source process that we are ready
	struct vfs_ReqData* reply = (struct vfs_ReqData*) mattiseAllocMessageSz( sizeof( struct vfs_ReqData ) );
	reply->msgcode = DEVFS_OPEN;
	reply->descriptor = getpid(); // this is the descriptor returned by open()
	reply->srcpid = myprocess;
	mattiseSendMessage( myprocess, DEVFS_OPEN, (char*) reply, sizeof( struct vfs_ReqData ) );

	// enter our message loop
	while( 1 )
	{
		// do we have a message?
		if( mattiseHasMessage() )
		{
			// get the message
			mbuff = mattiseRecvMessage( &myMsg );
			
			// the request data
			struct vfs_ReqData* myRequest = (struct vfs_ReqData*) mbuff;
			struct vfs_ReqData* myRequestRep = (struct vfs_ReqData*) mattiseAllocMessageSz( sizeof( struct vfs_ReqData ) );
			
			// handle it
			if( myMsg.msgcode != DEVFS_CLOSE )
			{
				// forward the message onto the handler
				//myRequest->descriptor = int_desc;
				memcpy( myRequestRep, myRequest, sizeof( struct vfs_ReqData ) );
				mattiseSendMessage( handlerpid, myMsg.msgcode, (char*) myRequestRep, sizeof( struct vfs_ReqData ) );
			}
			else
			{
				// we're now done, so forward the message and quit
				// this is seperate because of the quit part
				
				// forward the message
				//myRequest->descriptor = int_desc;
				memcpy( myRequestRep, myRequest, sizeof( struct vfs_ReqData ) );
				mattiseSendMessage( handlerpid, DEVFS_CLOSE, (char*) myRequestRep, sizeof( struct vfs_ReqData ) );

				// free the buffer
				mattiseFreeMessage( mbuff );
				
				// we're done here, quit
				break;
			}
			
			// free the buffer
			mattiseFreeMessage( mbuff );
		}
	}
	while( 1 );
}

// Entry point
int main( int argc, char* argv[] )
{
	// tell the VFS we're here
	struct vfs_Action* msg = (struct vfs_Action*) mattiseAllocMessageSz( sizeof( struct vfs_Action ) );
	msg->msgcode = DEVFS_MOUNT;
	msg->nmlen = sprintf( msg->filenm, "/dev/" );
	msg->vfsArg1 = msg->vfsArg2 = 0;
	msg->vfsArg3 = getpid();
	
	// send it
	mattiseSendMessage( 1, DEVFS_MOUNT, (char*) msg, sizeof( struct vfs_Action ) );
	
	// message buffer
	struct RecvMsgData myMsg;
	
	// null out the device list
	memset( (char*) deviceList, 0, sizeof( struct devfs_Device ) * 1024 );
	
	// main loop
	while( 1 )
	{
		// do we have a message?
		if( mattiseHasMessage() )
		{
			// get the message
			char* mbuff = mattiseRecvMessage( &myMsg );
			
			// storage for the data
			struct vfs_ReqData* req = (struct vfs_ReqData*) mbuff;
			struct vfs_ReqData* rep = (struct vfs_ReqData*) mattiseAllocMessageSz( sizeof( struct vfs_ReqData ) );
			
			switch( myMsg.msgcode )
			{
				// DEVFS_OPEN: handles opening a device for use, returns an internal descriptor
				case DEVFS_OPEN:
				{
					// get the return process id
					int retid = req->srcpid;
					
					// find a device
					int i;
					for( i = 0; i < 1024; i++ )
					{
						if( deviceList[i].state )
						{
							// check the device name
							if( strcmp( req->data + strlen( "/dev/" ), deviceList[i].nm ) == 0 )
								break;
						}
					}
					
					// check for a valid answer
					if( i == 1024 )
					{
						// no
						rep->msgcode = DEVFS_REPMSG;
						rep->descriptor = -1; // we couldn't open the thing!
						rep->datlen = 0;
					}
					else
					{
						// yes, return the index
						rep->msgcode = DEVFS_REPMSG;
						rep->descriptor = i;
						rep->datlen = 0;
					}
					
					// get the thread together
					if( rep->descriptor == -1 )
					{
						// return -1 as the descriptor
						rep->msgcode = DEVFS_REPMSG;
						rep->descriptor = -1;
						mattiseSendMessage( retid, rep->msgcode, (char*) rep, sizeof( struct vfs_ReqData ) );
						
						// all done
						break;
					}
					else
					{
						// create it
						rep->descriptor = createthread( (int) devfs_handler, 2, req->data, 10 );
					}
					
					// check it
					if( rep->descriptor == -1 )
					{
						// return a null descriptor
						rep->msgcode = myMsg.msgcode;
						rep->descriptor = -1;
						mattiseSendMessage( req->srcpid, myMsg.msgcode, (char*) rep, sizeof( struct vfs_ReqData ) );
					}
					
					// send it off
					rep->reqArg1 = deviceList[i].handler_pid;
					rep->srcpid = retid;
					mattiseSendMessage( rep->descriptor, DEVFS_REPMSG, (char*) rep, sizeof( struct vfs_ReqData ) );
				}
				break;
				
				// everything else
				case DEVFS_READ:
				case DEVFS_WRITE:
				case DEVFS_SEEK:
				case DEVFS_GETC:
				case DEVFS_PUTC:
				case DEVFS_CLOSE:
				case DEVFS_STAT:
				{
					
					// valid descriptor?
					if( req->descriptor == -1 )
					{
						// reply...
						memcpy( rep, req, sizeof( struct vfs_ReqData ) );
						rep->msgcode = myMsg.msgcode;
						rep->datlen = 0;
						mattiseSendMessage( rep->srcpid, myMsg.msgcode, (char*) rep, sizeof( struct vfs_ReqData ) );
					}
					else
					{				
						// send the request
						mattiseSendMessage( req->descriptor, myMsg.msgcode, (char*) rep, sizeof( struct vfs_ReqData ) );
					}
				}
				break;
				
				// DEVFS_MOUNT: handles installation of a new device
				// this does not need to tell the device that we
				// were able to install it
				case DEVFS_MOUNT:
				{
					// this will hold the found one, and a loop index
					int found = -1,i;
					
					// find a free entry in the list
					for( i = 0; i < 1024; i++ )
						if( deviceList[i].state == 0 )
						{
							found = i;
							break;
						}
					
					// was one found?
					if( found != -1 )
					{
						// yes! fill in the structure
						deviceList[found].handler_pid = req->srcpid;
						deviceList[found].nmlen = sprintf( deviceList[found].nm, "%s", req->data );
						deviceList[found].state = 1;
					}
				}
				break;
			}
			
			// free the message
			mattiseFreeMessage( mbuff );
		}
	}

	// return success
	return 0;
}
